// @Todo: 这里要看一下loadHash的实现， 
// 这里使用 默认参数 的形式避免定义全局变量(后面直接使用已经创建好的)，妙啊；前面的reduce()函数也有类似的功能
const deepClone = (target, cache = new Map()) => {

  // typeof Function 返回的是'function'， typeof undefined是'function', null是'object'， 
  // 其他的都是对应的基础类型(string/number/object/symbol/bigInt)
  const isObject = (obj) => typeof obj === 'object' && obj !== null

  if (isObject(target)) {
    // 解决循环引用
    const cacheTarget = cache.get(target)
    if (cacheTarget) {
      return cacheTarget
    }
    // 设置缓存
    cache.set(target, cloneTarget)

    let cloneTarget = Array.isArray(target) ? [] : {}
    for (const key in target) {
      const value = target[ key ] 
      if(target.hasOwnProperty(key)){
        // 自身属性
        cloneTarget[ key ] = isObject(value) ? deepClone(value, cache) : value
      }else{
        // 原型属性
        cloneTarget[ key ] = value
      } 
    }

    return cloneTarget
  } else {
    return target
  }
}

// @Todo： 1.自身属性； 2.原型属性； 3.Symbol属性； 4.不可枚举属性
const deepClone2 = (target, cache = new Map()) => {
  const isObject = (obj) => typeof obj === 'object' && obj !== null
  // 手动实现的foreach
  const forEach = (array, cb) => {
    const leng = array.length
    let i = -1

    while (++i < leng) {
      cb(array[ i ])
    }
  }

  if (isObject(target)) {
    const cacheObj = cache.get(target)

    if (cacheObj) {
      return cacheObj
    }

    let cloneTarget = Array.isArray(target) ? [] : {}
    let keys = Object.keys(target)

    cache.set(target, cloneTarget)

    forEach(keys, (key) => {
      const value = target[ key ]
      cloneTarget[ key ] = isObject(value) ? deepClone2(value, cache) : value
    })

    return cloneTarget
  } else {
    return target
  }
}

const target = {
  field1: 1,
  field2: undefined,
  field3: {
      child: 'child'
  },
  field4: [2, 4, 8],
  f: { f: { f: { f: { f: { f: { f: { f: { f: { f: { f: { f: {} } } } } } } } } } } },
};

target.target = target;

console.time();
const result1 = deepClone(target);
console.log(result1)
console.timeEnd();

console.time();
const result2 = deepClone2(target);
console.log(result2)
console.timeEnd();